#include "main.h"

HDC			hDC=NULL;
HGLRC		hRC=NULL;
HWND		hWnd=NULL;
HINSTANCE	hInstance;


//#################################
//-------------- global variables #
//#################################

bool	keys[256];
bool	active=true;
bool	fullscreen;

char    commands[80];

bool    shoot;

char *nomemap[]={
	"Maps\\map1.txt\0",
	"Maps\\map2.txt\0",
	"Maps\\map3.txt\0",
	"Maps\\map4.txt\0",
	"Maps\\map5.txt\0",
	"Maps\\map6.txt\0",
	"Maps\\map7.txt\0",
	"Maps\\map8.txt\0",
	"Maps\\map9.txt\0",
	"Maps\\map10.txt\0"
};

int           num_save=0;
char          SaveToPath[40];

bool          done=false;

long          mx,my;

float         PPL_s, PPL_t, PPL_r;
const float   Light_Radius = 100.0f;
const float   Light_Color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float         Light_X, Light_Y, Light_Z;

GLfloat       rotation=0.1f;

int dxs[8];
int dys[8];

GLuint texture[17];

//#################################
//------------------------- lists #
//#################################

GLuint base;
GLuint rifle_list;
GLuint ortho_list;
GLuint perspective_list;
GLuint floor_list;
GLuint stencil_list;
GLuint setup_register_combs;

//#################################
//----------------------- objects #
//#################################

PLAYER Io;
VISTA Visuale;
CCC cdist[8];	// Array for enemy direction
ENEMY enemy[MAXENEMY];	// ENEMY
BULLET bul[MAXBULLET];		// Bullet Array
BULLET par[MAXBULLET];		// For Bullet Explosion
BULLET espl[MAXEXPLOSION];	// Explosion Array
MAP map[MAPX][MAPZ];	// MAP
VAR var;
GFX set;
MD2 Bauul;	
Weapon  rifle;
Weapon  pulser;
Weapon  PlasmaGun;
Weapon  FireGun;
FSOUND_STREAM	*sound[1];

//#################################
//-------------------- prototypes #
//#################################

int  LoadMap(void);

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

PFNGLMULTITEXCOORD2FARBPROC		glMultiTexCoord2fARB	 = 0;
PFNGLACTIVETEXTUREARBPROC		glActiveTextureARB		 = 0;
PFNGLCLIENTACTIVETEXTUREARBPROC	glClientActiveTextureARB = 0;

PFNGLCOMBINERPARAMETERFVNV glCombinerParameterfvNV;
PFNGLCOMBINERPARAMETERIVNV glCombinerParameterivNV;
PFNGLCOMBINERPARAMETERFNV glCombinerParameterfNV;
PFNGLCOMBINERPARAMETERINV glCombinerParameteriNV;
PFNGLCOMBINERINPUTNV glCombinerInputNV;
PFNGLCOMBINEROUTPUTNV glCombinerOutputNV;
PFNGLFINALCOMBINERINPUTNV glFinalCombinerInputNV;

PFNGLGETCOMBINERINPUTPARAMETERFVNV glGetCombinerInputParameterfvNV;
PFNGLGETCOMBINERINPUTPARAMETERIVNV glGetCombinerInputParameterivNV;
PFNGLGETCOMBINEROUTPUTPARAMETERFVNV glGetCombinerOutputParameterfvNV;
PFNGLGETCOMBINEROUTPUTPARAMETERIVNV glGetCombinerOutputParameterivNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERFVNV glGetFinalCombinerInputParameterfvNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERIVNV glGetFinalCombinerInputParameterivNV;

PFNGLFOGCOORDFEXTPROC glFogCoordfEXT = 0;

PFNGLGENPROGRAMSNVPROC        glGenProgramsNV = 0;
PFNGLLOADPROGRAMNVPROC        glLoadProgramNV = 0;
PFNGLBINDPROGRAMNVPROC        glBindProgramNV = 0;
PFNGLTRACKMATRIXNVPROC        glTrackMatrixNV = 0;
PFNGLPROGRAMPARAMETER4FNVPROC glProgramParameter4fNV = 0;

//#################################
//-------------------------macros #
//#################################

#define Contact(X1,Y1,Z1,X2,Y2,Z2) sqrt( (X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2)+(Z1-Z2)*(Z1-Z2) )
#define VectorLength( v ) sqrt(v.x * v.x + v.y * v.y + v.z * v.z)

//#################################
//---------------- math functions #
//#################################

Vector VectorNormalize( Vector v )
{
	Vector vresult;
	double l = VectorLength( v );

	vresult.x = v.x/l;
	vresult.y = v.y/l;
	vresult.z = v.z/l;

	return vresult;
}

Vector VectorMultiply( Vector v1, Vector v2 )
{
	Vector vresult;

	vresult.x = v1.y * v2.z - v1.z * v2.y;
	vresult.y = v1.z * v2.x - v1.x * v2.z;
	vresult.z = v1.x * v2.y - v1.y * v2.x;

	return vresult;
}

Vector VectorScalarMultiply( Vector v, double s )
{
	Vector vresult;

	vresult.x = v.x * s;
	vresult.y = v.y * s;
	vresult.z = v.z * s;

	return vresult;
}

Vector VectorDiff( Vector v1, Vector v2 )
{
	Vector vresult;

	vresult.x = v1.x - v2.x;
	vresult.y = v1.y - v2.y;
	vresult.z = v1.z - v2.z;

	return vresult;
}	

bool Visible(double xa,double za,double xb,double zb)	// True if the quad is in front
{
	double la,lb;
	double sa,sb,ca,cb;
	double tga,tgb;
	double a,b,delta;
	double xpos,zpos;
	double k=3.141/180;

	xpos=-cos(Visuale.anx*k);
	zpos=+sin(Visuale.anx*k);
	xa=xa-(Io.x+xpos);
	za=za-(Io.z+zpos);
	xb=xb-(Io.x+xpos);
	zb=zb-(Io.z+zpos);
	
	la=sqrt(xa*xa+za*za);
	lb=sqrt(xb*xb+zb*zb);
	if(la==0)
		return(true);
	if(lb==0)
		return(true);
	sa=za/la;
	ca=xa/la;
	sb=zb/lb;
	cb=xb/lb;
	tga=sa/ca;
	tgb=sb/cb;
	a=atan(tga);
	b=atan(tgb);
	a=a*180/3.141;
	b=b*180/3.141;

	if(sa>0)
		if(ca<0)
			a=a+180;
	if(sa<0)
	{
		if(ca>0)
			a=a+360;
		else
			a=a+180;
	}
	if(sb>0)
		if(cb<0)
			b=b+180;
	if(sb<0)
	{
		if(cb>0)
			b=b+360;
		else
			b=b+180;
	}
	delta=b-a;
	if(delta<0)
		delta+=360;
	if( (delta>90) && (delta<270) )
		return(false);

	return(true);
}																	

//#################################
//--------------- file operations #
//#################################

void LoadIni(void)
{
	FILE *ptr;
	char s[40];

	ptr=fopen("game.ini","r");
	if(ptr!=NULL)
	{
		do
		{
			fgets(s,40,ptr);
			if(strcmp(s,"[width]\n")==0)
			{
				fgets(s,40,ptr);
				set.width=atoi(s);
			}
			if(strcmp(s,"[height]\n")==0)
			{
				fgets(s,40,ptr);
				set.height=atoi(s);
			}
			if(strcmp(s,"[depth]\n")==0)
			{
				fgets(s,40,ptr);
				set.depth=atoi(s);
			}
            if(strcmp(s,"[fullscreen]\n")==0)
			{
				fgets(s,40,ptr);
				set.fullscreen=atoi(s);
			}
            if(strcmp(s,"[use compressed textures]\n")==0)
			{
				fgets(s,40,ptr);
				set.s3tc=atoi(s);
			}
			if(strcmp(s,"[frustum culling]\n")==0)
			{
				fgets(s,40,ptr);
				set.cull_map=atoi(s);
			}
			if(strcmp(s,"[view distance]\n")==0)
			{
				fgets(s,40,ptr);
				set.cull_dist=(float)(atof(s));
			}
			if(strcmp(s,"[floor reflections]\n")==0)
			{
				fgets(s,40,ptr);
				set.floor_mirror=atoi(s);
			}
			if(strcmp(s,"[motion blur effects]\n")==0)
			{
				fgets(s,40,ptr);
				set.draw_blur=atoi(s);
			}
			if(strcmp(s,"[use per-pixel lighting]\n")==0)
			{
				fgets(s,40,ptr);
				set.ppl=atoi(s);
			}
			if(strcmp(s,"[use fog coords]\n")==0)
			{
				fgets(s,40,ptr);
				set.fogcoords=atoi(s);
			}
		}while(!feof(ptr));
		fclose(ptr);
	}
}

void SaveGame()
{
					ofstream tfile(SaveToPath);

					int map_num=var.mapnumber; 

					int player_health=Io.health;
					int player_stamina=Io.stamina;

					int player_weapons=Io.nguns;

					float player_x=Io.x;
					float player_y=Io.y;
					float player_z=Io.z;
					
					tfile << map_num 
						  << " "
						  << player_health
						  << " "
						  << player_stamina
						  << " "
						  << player_weapons
						  << " "
						  << player_x
						  << " "
						  << player_y
						  << " "
						  << player_z;
						
					tfile.close(); 
} 

void LoadGame()
{      
	                int map_num;
					int player_health;
					int player_stamina;

					int player_weapons;

					float player_x;
					float player_y;
					float player_z;
	
	                ifstream tfile(SaveToPath);  //opens the file for reading no file not found error recovering yet
							
					tfile >> map_num; 
					tfile >> player_health;
					tfile >> player_stamina;
					tfile >> player_weapons;
                    tfile >> player_x;
                    tfile >> player_y;
					tfile >> player_z;

					tfile.close();
					   
					var.mapnumber=map_num;
					Io.health=player_health;
					Io.stamina=player_stamina;

					Io.nguns=player_weapons;

					/*Io.x=player_x;
					Io.y=player_y;
					Io.z=player_z;*/
					LoadMap();

}

//#################################
//--------------- fps calculation #
//#################################

float GetFPS(void) 
{
	static float frames_per_second = 60;
	static int starttime,endtime,counter;

	if(counter == 10) 
	{
		endtime = starttime;
		starttime = GetTickCount();
		frames_per_second = counter * 1000.0 / (float)(starttime - endtime);
		counter = 0;
	}
	counter++;
	return frames_per_second;
}

//#################################
//--------------- init extensions #
//#################################

bool Init_MultiTexturing()
{
    
	// The name of the extension
	char *Extension;

    Extension = (char *) glGetString(GL_EXTENSIONS);
   
    if (!(strstr(Extension, "GL_ARB_multitexture")) == 1)
		{ 
			// The "GL_ARB_multitexture" extension is not supported
			MessageBox(NULL,"'GL_ARB_multitexture'","ERROR : Extension not supported :",MB_ICONERROR);
		} 
	
	else 
		{
			// The "GL_ARB_multitexture" is supported
			
			// MultiTexturing Initialisation
			glMultiTexCoord2fARB     =  (PFNGLMULTITEXCOORD2FARBPROC)     wglGetProcAddress("glMultiTexCoord2fARB");
			glActiveTextureARB       =  (PFNGLACTIVETEXTUREARBPROC)       wglGetProcAddress("glActiveTextureARB");		
			glClientActiveTextureARB =  (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress("glClientActiveTextureARB");
      
			// MultiTexturing Initialisation failed !
			if (!glActiveTextureARB || !glMultiTexCoord2fARB || !glClientActiveTextureARB)
				{
					MessageBox(NULL,"GL_ARB_multitexture","ERROR : Initialisation failed !",MB_ICONERROR);
					
					return false;
				}
		}

	return true;

}

bool Init_Register_Combiners()	// Register Combiners Initialisation
{
	// The name of the extension
	char *Extension;

    Extension = (char *) glGetString(GL_EXTENSIONS);
   
    if (!(strstr(Extension, "GL_NV_register_combiners")) == 1)
		{ 
			// The "GL_NV_register_combiners" extension is not supported
			MessageBox(NULL,"GL_NV_register_combiners","Extension not supported :",MB_ICONERROR);
			
			return false;
		} 
	
	glCombinerParameterfvNV = (PFNGLCOMBINERPARAMETERFVNV) wglGetProcAddress("glCombinerParameterfvNV");
	glCombinerParameterivNV = (PFNGLCOMBINERPARAMETERIVNV) wglGetProcAddress("glCombinerParameterivNV");
	glCombinerParameterfNV  = (PFNGLCOMBINERPARAMETERFNV) wglGetProcAddress("glCombinerParameterfNV");
	glCombinerParameteriNV  = (PFNGLCOMBINERPARAMETERINV) wglGetProcAddress("glCombinerParameteriNV");
	glCombinerInputNV       = (PFNGLCOMBINERINPUTNV) wglGetProcAddress("glCombinerInputNV");
	glCombinerOutputNV      = (PFNGLCOMBINEROUTPUTNV) wglGetProcAddress("glCombinerOutputNV");
	glFinalCombinerInputNV  = (PFNGLFINALCOMBINERINPUTNV) wglGetProcAddress("glFinalCombinerInputNV");
	
	glGetCombinerInputParameterfvNV      = (PFNGLGETCOMBINERINPUTPARAMETERFVNV) wglGetProcAddress("glGetCombinerInputParameterfvNV");
	glGetCombinerInputParameterivNV      = (PFNGLGETCOMBINERINPUTPARAMETERIVNV) wglGetProcAddress("glGetCombinerInputParameterivNV");
	glGetCombinerOutputParameterfvNV     = (PFNGLGETCOMBINEROUTPUTPARAMETERFVNV) wglGetProcAddress("glGetCombinerOutputParameterfvNV");
	glGetCombinerOutputParameterivNV     = (PFNGLGETCOMBINEROUTPUTPARAMETERIVNV) wglGetProcAddress("glGetCombinerOutputParameterivNV");
	glGetFinalCombinerInputParameterfvNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERFVNV) wglGetProcAddress("glGetFinalCombinerInputParameterfvNV");
	glGetFinalCombinerInputParameterivNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERIVNV) wglGetProcAddress("glGetFinalCombinerInputParameterivNV");

	return true;
}

void InitFog(void)
{
	if(set.fogcoords==false)
	{
	if(set.cull_map==true)
	{
	 const GLfloat fgcolor[]=		{ 0.0f, 0.0f, 0.0f,1.0f };

	 glFogfv(GL_FOG_COLOR,fgcolor);
	 glFogf(GL_FOG_START,set.cull_dist-(set.cull_dist/2));
	 glFogf(GL_FOG_END,set.cull_dist-(set.cull_dist/3));
	 glFogf(GL_FOG_MODE,GL_LINEAR);
	 glFogf(GL_FOG_DENSITY, 0.3f);
	 glEnable(GL_FOG);
	}
	else
		glDisable(GL_FOG);
	}
	else
	{
		const GLfloat	fogColor[4] = {0.5f, 0.5f, 0.55f, 1.0f};							// Fog Colour 

		glFogCoordfEXT = (PFNGLFOGCOORDFEXTPROC) wglGetProcAddress("glFogCoordfEXT");

		glEnable(GL_FOG);													// Enable Fog
	    glFogi(GL_FOG_MODE, GL_LINEAR);										// Fog Fade Is Linear
	    glFogfv(GL_FOG_COLOR, fogColor);									// Set The Fog Color
	    glFogf(GL_FOG_START,  0.5f);										// Set The Fog Start
	    glFogf(GL_FOG_END,    0.0f);										// Set The Fog End
	    glHint(GL_FOG_HINT, GL_NICEST);										// Per-Pixel Fog Calculation
	    glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT);		// Set Fog Based On Vertice Coordinates
	}


}
/*
bool InitVertexPrograms()
{
	char *Extension;

    Extension = (char *) glGetString(GL_EXTENSIONS);
   
    if (!(strstr(Extension, "GL_NV_vertex_program")) == 1)
	{ 
			MessageBox(NULL,"GL_NV_vertex_program","Extension not supported :",MB_ICONERROR);
			return false;
	}

   glGenProgramsNV(1, &progID);
   glBindProgramNV(GL_VERTEX_PROGRAM_NV, progID);
   glLoadProgramNV(GL_VERTEX_PROGRAM_NV, progID, strlen((char *) vprog::vtx_prog), vprog::vtx_prog);
   // Combination of modelview and projection into c[0] to c[3]
   glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);
   // ModelView into c[4] to c[7]
   glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_IDENTITY_NV);

   //delete [] vtx_prog; // release program, we are done with it
   return true;
}
*/
//#################################
//-------------------- my console #
//#################################

void CheckCommand(char commands[80], HWND dlg)
{
	if(strstr(commands,"exit "))
		keys[VK_ESCAPE]=true;
	if(strstr(commands,"cull_map "))
	{
		set.cull_map=!set.cull_map;
		InitFog();
	}
	if(strstr(commands,"new_gat "))
	{
		if(Io.nguns<4)
		    Io.nguns++;

		rifle.ammo=200;
		pulser.ammo=75;
		PlasmaGun.ammo=40;
		FireGun.ammo=40;
	}
	if(strstr(commands,"floor_reflections "))
	{
		set.floor_mirror=!set.floor_mirror;
	}
	if(strstr(commands,"enemy_health "))
	{
		for(int i=0; i<var.nenemy;i ++)
		{
			char cur_health[10];
			sprintf(cur_health, "%i ", enemy[i].health);
			strcat(commands, cur_health);
			SetDlgItemText(dlg, IDC_COMMANDS, (LPTSTR)commands);
		}
	}
	if(strstr(commands,"motion_blur "))
	{
		set.draw_blur=!set.draw_blur;
	}
	if(strstr(commands,"save_new "))
	{
		num_save++;
		sprintf(SaveToPath, "Save//sav%i.sav", num_save);
		SaveGame();
	}
	if(strstr(commands,"save_current "))
	{
		SaveGame();
	}
	if(strstr(commands,"load "))
	{
		if(num_save==0)
			num_save++;

		sprintf(SaveToPath, "Save//sav%i.sav", num_save);
		LoadGame();
	}
	if(strstr(commands,"kill_player "))
	{
		Io.health=0;
	}
	if(strstr(commands,"reanimate_player "))
	{
       Io.health=100;
	   Io.active=true;
	}
	if(strstr(commands,"per_pixel_lighting "))
	{
	   if(!Init_Register_Combiners())
		set.ppl=false;
	   else
        set.ppl=!set.ppl;
	}
	if(strstr(commands,"fog_coords "))
	{
		set.fogcoords=!set.fogcoords;
		InitFog();
	}
	else
     SetDlgItemText(dlg, IDC_COMMANDS, (LPTSTR)commands);
}


BOOL CALLBACK ConsoleProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_INITDIALOG:
		glEnable(GL_SCISSOR_TEST);
		var.show_console=true;
		ShowCursor(true);
		SetDlgItemText(hwnd, IDC_COMMANDS, (LPTSTR)commands);
		return true;
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDOK:
			GetDlgItemText(hwnd, IDC_COMMANDS, (LPTSTR)commands, 80);
			strcat(commands, " ");
			CheckCommand(commands, hwnd);
			return true;
		case ID_CLOSE_CONSOLE:
			var.show_console=false;
			glDisable(GL_SCISSOR_TEST);
			EndDialog(hwnd, 0);
			return true;
		}
		return 0;
	case WM_CLOSE:
		 var.show_console=false;
         EndDialog(hwnd, IDOK);
		 glDisable(GL_SCISSOR_TEST);
         return true;
	}
	
	return false;
}

int ShowConsole(void)
{

	HWND dlg = CreateDialog(hInstance,                    // instance handle;
                           MAKEINTRESOURCE(IDD_CONSOLE), // dialog identifier;
                           hWnd,                   // parent/owner handle;
                           ConsoleProc);                     // window procedure;

   if(dlg==NULL) 
   {
          return false;
   }
   else 
   {
	   ShowWindow(dlg,SW_SHOW);
	   SwapBuffers(hDC);
	   return true;
   }
}

//#################################
//------------------ sound system #
//#################################

void CreateSound(char name[80], int num)
{
	sound[num] =  FSOUND_Stream_OpenFile(name, FSOUND_HW3D || FSOUND_LOOP_OFF,0);
}

void InitSound(void)
{
	FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND);
	FSOUND_SetBufferSize(50);
	FSOUND_SetDriver(0);
	FSOUND_Init(44100, 32, 0);

	CreateSound("shutdown.wav", 0);
	CreateSound("Data//hurt.wav", 1);
}

void BindSound(int num)
{
	FSOUND_Stream_Play(FSOUND_FREE, sound[num]);
}

//#################################
//-------------------------- font #
//#################################

GLvoid BuildFont(GLvoid)								// Build Our Font Display List
{
	float	cx;											// Holds Our X Character Coord
	float	cy;											// Holds Our Y Character Coord

	base=glGenLists(256);								// Creating 256 Display Lists
	glBindTexture(GL_TEXTURE_2D, texture[0]);			// Select Our Font Texture
	for (int loop=0; loop<256; loop++)						// Loop Through All 256 Lists
	{
		cx=float(loop%16)/16.0f;						// X Position Of Current Character
		cy=float(loop/16)/16.0f;						// Y Position Of Current Character

		glNewList(base+loop,GL_COMPILE);				// Start Building A List
			glBegin(GL_QUADS);							// Use A Quad For Each Character
				glTexCoord2f(cx,1-cy-0.0625f);			// Texture Coord (Bottom Left)
				glVertex2i(0,0);						// Vertex Coord (Bottom Left)
				glTexCoord2f(cx+0.0625f,1-cy-0.0625f);	// Texture Coord (Bottom Right)
				glVertex2i(16,0);						// Vertex Coord (Bottom Right)
				glTexCoord2f(cx+0.0625f,1-cy);			// Texture Coord (Top Right)
				glVertex2i(16,16);						// Vertex Coord (Top Right)
				glTexCoord2f(cx,1-cy);					// Texture Coord (Top Left)
				glVertex2i(0,16);						// Vertex Coord (Top Left)
			glEnd();									// Done Building Our Quad (Character)
			glTranslated(10,0,0);						// Move To The Right Of The Character
		glEndList();									// Done Building The Display List
	}													// Loop Until All 256 Are Built
}

GLvoid KillFont(GLvoid)									// Delete The Font From Memory
{
	glDeleteLists(base,256);							// Delete All 256 Display Lists
}

GLvoid glPrint(GLint x, GLint y, float dim, float r, float g, float b, char *string, int set)	// Where The Printing Happens
{
	if (set>1)
		set=1;


	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE,GL_ONE);

	glBindTexture(GL_TEXTURE_2D, texture[1]);			// Select Our Font Texture
	glDisable(GL_DEPTH_TEST);							// Disables Depth Testing

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glPushMatrix();										// Store The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	glOrtho(0,1024,0,768,-1,1);							// Set Up An Ortho Screen
	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix

	glPushMatrix();										// Store The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
	glTranslated(x,y,0);								// Position The Text (0,0 - Bottom Left)
	glListBase(base-32+(128*set));						// Choose The Font Set (0 or 1)
	glColor3f(r,g,b);
	glScalef(dim,dim,dim);
	glCallLists(strlen(string),GL_BYTE,string);			// Write The Text To The Screen
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glPopMatrix();										// Restore The Old Projection Matrix

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glPopMatrix();										// Restore The Old Projection Matrix

	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDisable(GL_BLEND);
}

//#################################
//------------ masking operations #
//#################################

void DrawIcon(int tex_num,float r, float g, float b,float x, float y,float sizeX, float sizeY)//float dim, float x, float y)
{	
	glCallList(ortho_list);

    if(sizeX<0)
     sizeX=0;
    if(sizeY<0)
     sizeY=0;

	glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);

	glEnable(GL_BLEND);											
	glBlendFunc(GL_ONE,GL_ONE);

	glBindTexture(GL_TEXTURE_2D,  texture[tex_num]);

	glBegin(GL_QUADS);
	    glColor4f(r, g, b, 1);
		glTexCoord2f(0.0f, 1.0f);	glVertex2f(x, y);   
		glTexCoord2f(0.0f, 0.0f);	glVertex2f(x, y+sizeY);
		glTexCoord2f(1.0f, 0.0f);	glVertex2f(x+sizeX, y+sizeY);
		glTexCoord2f(1.0f, 1.0f);	glVertex2f(x+sizeX, y);
	glEnd();

	glDisable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);

	glCallList(perspective_list);
}

bool AnimateNextFrame(int desiredFrameRate)
{
	static float lastTime = 0.0f;
	float elapsedTime = 0.0;

	// Get current time in seconds  (milliseconds * .001 = seconds)
    float currentTime = GetTickCount() * 0.001f; 

	// Get the elapsed time by subtracting the current time from the last time
	elapsedTime = currentTime - lastTime;

	// Check if the time since we last checked is over (1 second / framesPerSecond)
    if( elapsedTime > (1.0f / desiredFrameRate) )
    {
		// Reset the last time
        lastTime = currentTime;	

		// Return TRUE, to animate the next frame of animation
        return true;
    }

	// We don't animate right now.
	return false;
}

void DrawBlur(int num)
{
    glPushMatrix();

		glDisable(GL_DEPTH_TEST);			

		glBlendFunc(GL_SRC_ALPHA,GL_ONE);	
		glEnable(GL_BLEND);					

		glBindTexture(GL_TEXTURE_2D, texture[num]);		

		glColor4f(1, 1, 1, 0.6f);

		glCallList(ortho_list);

		glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 1.0f);	glVertex2f(0, 0);
			glTexCoord2f(0.0f, 0.0f);	glVertex2f(0, set.height);
			glTexCoord2f(1.0f, 0.0f);	glVertex2f(set.width, set.height);
			glTexCoord2f(1.0f, 1.0f);	glVertex2f(set.width, 0);
		glEnd();

		glCallList(perspective_list);

		glEnable(GL_DEPTH_TEST);						
		glDisable(GL_BLEND);							

	glPopMatrix();
}

void DrawSprite(double x, double y, double z,double dimensione,float r,float g,float b,float iox,float ioy,float ioz,int Tex)
{
	Vector Position;
	Vector MyPosition;
	Position.x = x;
	Position.y = y;
	Position.z = z;

	MyPosition.x=iox;
	MyPosition.y=ioy;
	MyPosition.z=ioz;
	Vector sight = VectorDiff(MyPosition, Position);

	Vector cz;
	cz.x = 0;
	cz.y = 0;
	cz.z = 1;

	Vector cross1 = VectorMultiply( sight, cz );
	Vector cross2 = VectorMultiply( sight, cross1 );

	cross1 = VectorNormalize(cross1);
	cross2 = VectorNormalize(cross2);

	cross1 = VectorScalarMultiply(cross1, dimensione);
	cross2 = VectorScalarMultiply(cross2, dimensione);

	glColor3f(r,g,b);
	glEnable (GL_BLEND);
	glBlendFunc( GL_ONE,GL_ONE);
	glDepthMask (GL_FALSE);

	glBindTexture( GL_TEXTURE_2D, texture[Tex] );
	glBegin(GL_QUADS);
		glTexCoord2d( 0.0, 0.0 );
		glVertex3d( Position.x + cross1.x, Position.y + cross1.y, Position.z + cross1.z);
		glTexCoord2d( 1.0, 0.0 );
		glVertex3d( Position.x - cross2.x, Position.y - cross2.y, Position.z - cross2.z);
		glTexCoord2d( 1.0, 1.0 );
		glVertex3d( Position.x - cross1.x, Position.y - cross1.y, Position.z - cross1.z);
		glTexCoord2d( 0.0, 1.0 );
		glVertex3d( Position.x + cross2.x, Position.y + cross2.y, Position.z + cross2.z);
	glEnd();

	glDisable (GL_BLEND);
	glDepthMask (GL_TRUE);
}

void DrawPanel(void)
{
	char s[80];

	DrawIcon(10,0.4f,0.4f,0.4f,0,0, set.width, set.height);

	sprintf(s,"Health : %d\0",Io.health);
	glPrint(set.width/50,set.height-(set.height/4),2,1,1,1,s,0);

	sprintf(s,"Enemy  : %d\0",var.nenemy);
	glPrint(set.width/50,set.height-(set.height/3),2,1,1,1,s,0);

	sprintf(s,"Map    : %d\0",var.mapnumber);
	glPrint(set.width/50,set.height-(set.height/2.5),2,1,1,1,s,0);

	sprintf(s,"Stamina : %d\0",Io.stamina);
	glPrint(set.width/3,set.height-(set.height/4),2,1,1,1,s,0);
}

void DrawHud(void)
{
    glPrint(2,28,1,(float)40/Io.health,(float)Io.health/100,0,"health:",0);

	for(int i=1;i<=Io.health;i++)
	 DrawIcon(8,(float)40/i,(float)i/100,0,2+(5*i),740, 5, 20); //draw health bar

	glPrint(2,88,1,(float)40/Io.strength,(float)40/Io.strength,(float)Io.strength,"strength:",0);
    for(int j=1;j<=Io.stamina;j++)
	{
		if(Io.strength < j)
			DrawIcon(8,0.5f,0.5f,0.5f,2+(5*j),680, 5, 20); //draw pontential stamina bar
		else
	        DrawIcon(8,(float)40/j,(float)40/j,(float)j,2+(5*j),680, 5, 20); //draw actual stamina bar
	}

	float fps=GetFPS();
	char  fps_string[10];

	sprintf(fps_string,"fps: %f\0",fps );		
	glPrint(set.width/1.3f,set.height-64,1,10/fps,fps/10,0,fps_string,0);

	char s[3];

	if(Io.guntype==WEAPON_PULSER)
	{
	 sprintf(s,"%i",pulser.ammo);
	 DrawIcon(8,0,1,1,set.width*0.8f, set.height-100, set.width*0.1f, set.width*0.1f);
	}

	if(Io.guntype==WEAPON_RIFLE)
	{
	 sprintf(s,"%i",rifle.ammo);
	 DrawIcon(9,1,1,1,set.width*0.8f, set.height-100, set.width*0.1f, set.width*0.1f);
	}

	if(Io.guntype==WEAPON_PLASMAGUN)
	{
	 sprintf(s,"%i",PlasmaGun.ammo);
	 DrawIcon(16,0.3f,0.7f,1,set.width*0.8f, set.height-100, set.width*0.1f, set.width*0.1f);
	}

	if(Io.guntype==WEAPON_FIREGUN)
	{
	 sprintf(s,"%i",FireGun.ammo);
	 DrawIcon(16,1,0.6f,0,set.width*0.8f, set.height-100, set.width*0.1f, set.width*0.1f);
	}
	
	glPrint(set.width*0.9f,15,3,1.0f,1.0f,1.0f,s,0);

	if(var.panel==true)
	    DrawPanel();
}

//#################################
//---------generate p.p.l.-Coords #
//#################################

void ProcessVertex_PPL(float x, float y, float z)
{
	PPL_s = (x - Light_X) / (Light_Radius) + 0.5f;
	PPL_r = (y - Light_Y) / (Light_Radius) + 0.5f;
	PPL_t = (z - Light_Z) / (Light_Radius) + 0.5f;
	
	glMultiTexCoord2fARB(GL_TEXTURE0_ARB, PPL_s, PPL_t);
	glMultiTexCoord2fARB(GL_TEXTURE1_ARB, PPL_r, 0.5f);

	glVertex3f(x, y, z);
}

//#################################
//-------------- particle effects #
//#################################

void DrawExplosion(void)	// Draws Bomb Explosion
{
	int t;
	float col;

	BindSound(0);

	for(t=0;t<MAXEXPLOSION;t++)
	{
		// Draw

		if(var.tenemy==ENEMY_GHOST)
		{
		col=(float)espl[t].active/80;
		DrawSprite(espl[t].x,espl[t].y,espl[t].z,espl[t].an,col/2,col,col/2,Io.x,Io.y,Io.z,8);
		
		espl[t].an+=0.1f;
		// Move
		espl[t].x+=espl[t].dx;
		espl[t].y+=(cos(espl[t].dy))/4;
		espl[t].z+=espl[t].dz;

		espl[t].active-=3;
		}

		if(var.tenemy==ENEMY_FIRE)
		{
		col=(float)espl[t].active/100;
		DrawSprite(espl[t].x,espl[t].y,espl[t].z,2,col/2,col/4,0,Io.x,Io.y,Io.z,8);

		espl[t].x+=espl[t].dx;
		espl[t].y+=sin(espl[t].dy);
		espl[t].z+=espl[t].dz;

		espl[t].dy+=0.01f;

		espl[t].active-=3;
		}

		if(espl[t].active<=0)
			var.explosion=false;
	}
}

void InitExplosion(float cX, float cY, float cZ)
{
	int t,j;
	int k=MAXEXPLOSION/4;
	float dan=(float)360/k,an,dany=(float)45/4,any=0;
	float dx,dy,dz;
	float inc=5;

    //PlaySound("Explosion.wav",NULL,SND_FILENAME|SND_ASYNC);

	var.explosion=true;
	j=0;
	an=0;
	for(t=0;t<MAXEXPLOSION;t++)
	{
	   if(var.tenemy==ENEMY_GHOST)
	   {
		espl[t].active=80;
		espl[t].x=cX;
		espl[t].y=cY;
		espl[t].z=cZ;
		espl[t].an=(float)(rand()%100)/100;
		
		dx=cos(an*3.141/180)/inc;
		dy=sin(any*3.141/180)/5;
		dz=sin(an*3.141/180)/inc;

		espl[t].dx=dx;
		espl[t].dy=dy;
		espl[t].dz=dz;
		
		an+=dan;
		j++;
		if(j==k)
		{
			inc+=10*sin(any*3.141/180);
			j=0;
			an=0;
			any+=dany;
		}	
	   }
	   else
	   {
		espl[t].active=80;
		espl[t].x=cX;
		espl[t].y=cY;
		espl[t].z=cZ;
		espl[t].dx=(float)(rand()%10-rand()%10)/30;
		espl[t].dy=(float)(rand()%10-rand()%10)/60;
		espl[t].dz=(float)(rand()%10-rand()%10)/30;
	   }
	}
}

void InitBullet(void)
{
	int t;
	float xf,yf,zf;
	float dx,dy,dz;

	dx=cos( (Visuale.anx-10)*3.141/180 );
	dy=sin(-(Visuale.any+15)*3.141/180);
	dz=-sin( (Visuale.anx-10)*3.141/180 );
	xf=Io.x+dx*4.8f;
	yf=Io.y+dy*3.0f;
	zf=Io.z+dz*4.8f;
	dx=Io.x+20*cos(Visuale.anx*3.141/180);
	dy=Io.y-20*sin(Visuale.any*3.141/180);
	dz=Io.z-20*sin(Visuale.anx*3.141/180);
	dx=dx-xf;
	dy=dy-yf;
	dz=dz-zf;
	dx=dx/10;
	dy=dy/10;
	dz=dz/10;

	for(t=0;t<MAXBULLET;t++)
	{
		if(bul[t].active<=0)
		{
			bul[t].active=100;
			bul[t].x=xf;
			bul[t].y=yf;
			bul[t].z=zf;
			bul[t].an=Visuale.anx-90;
			bul[t].dx=dx;
			bul[t].dy=dy;
			bul[t].dz=dz;

			Visuale.any-=0.2f;
			Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
			
			return;
		}
	}
}

void InitFireBlast(float cX, float cY, float cZ )
{
	for(int j=0;j<5;j++)
	{
		for(int t=0;t<MAXBULLET;t++)
		{
			if(par[t].active<0)
			{
				par[t].active=300;
				par[t].x=cX;
				par[t].y=cY;
				par[t].z=cZ;
				par[t].dx=(float)(rand()%10-rand()%10)/30;
				par[t].dy=(float)(rand()%10-rand()%10)/60;
				par[t].dz=(float)(rand()%10-rand()%10)/30;
				break;
			}
		}
	}
}

void DrawFireBlast(void)	//fire blast :)
{
	float col;

	for(int t=0;t<MAXBULLET;t++)
	{
		if(par[t].active>=0)
		{
			par[t].active--;
			par[t].x+=par[t].dx;
			par[t].y+=sin(par[t].dy);
			par[t].z+=par[t].dz;
			par[t].dy+=0.01f;

			col=(float)par[t].active/100;
			DrawSprite(par[t].x,par[t].y,par[t].z,2,col/2,col/4,0,Io.x,Io.y,Io.z,8);
		}
	}
}

void DrawImpactSpray(void)	// For gun 1
{
	float col;

	for(int t=0;t<MAXBULLET;t++)
	{
		if(par[t].active>=0)
		{
			par[t].active--;
			par[t].x+=par[t].dx;
			par[t].y+=par[t].dy;
			par[t].z+=par[t].dz;
			par[t].dy-=0.01f;

			col=(float)par[t].active/100;
			DrawSprite(par[t].x,par[t].y,par[t].z,0.4,col,col,col/2,Io.x,Io.y,Io.z,9);
		}
	}
}

void DrawPlasmaWave(void)	// For gun 3
{
	int t;
	float col;

	for(t=0;t<MAXBULLET;t++)
	{
		if(par[t].active>=0)
		{
			par[t].active-=10;
			par[t].x+=par[t].dx;
			par[t].y+=par[t].dy;
			par[t].z+=par[t].dz;
			par[t].dy-=0.01f;

			col=(float)par[t].active/100;
			DrawSprite(par[t].x,par[t].y,par[t].z,0.2,col/2,col,col,Io.x,Io.y,Io.z,8);
		}
	}
}

void DrawFireBall(void)	// For gun 4
{
	int t;
	float col;

	for(t=0;t<MAXBULLET;t++)
	{
		if(par[t].active>=0)
		{
			par[t].active-=50;
			par[t].x+=par[t].dx;
			par[t].y+=par[t].dy;
			par[t].z+=par[t].dz;
			par[t].dy+=0.3f;

			col=(float)par[t].active/200;
			DrawSprite(par[t].x,par[t].y,par[t].z,1,col/2,col/4,0,Io.x,Io.y,Io.z,8);
		}
	}
}

void InitParticles1(int nb)
{
	for(int j=0;j<8;j++)
	{
		for(int t=0;t<MAXBULLET;t++)
		{
			if(par[t].active<0)
			{
				par[t].active=100;
				par[t].x=bul[nb].x;
				par[t].y=bul[nb].y;
				par[t].z=bul[nb].z;
				par[t].dx=(float)(rand()%10-rand()%10)/40;
				par[t].dy=(float)(rand()%10-rand()%10)/40;
				par[t].dz=(float)(rand()%10-rand()%10)/40;
				break;
			}
		}
	}
}

void InitParticles2(int nb)
{
	for(int j=0;j<5;j++)
	{
		for(int t=0;t<MAXBULLET;t++)
		{
			if(par[t].active<0)
			{
				par[t].active=300;
				par[t].x=bul[nb].x;
				par[t].y=bul[nb].y;
				par[t].z=bul[nb].z;
				par[t].dx=(float)(rand()%10-rand()%10)/30;
				par[t].dy=(float)(rand()%10-rand()%10)/60;
				par[t].dz=(float)(rand()%10-rand()%10)/30;
				break;
			}
		}
	}
}

void DrawParticles(void)
{
	switch(Io.guntype)
	{
	case WEAPON_RIFLE:
	 DrawImpactSpray();
	 break;
	case WEAPON_PLASMAGUN:
	 DrawPlasmaWave();
	 break;
	case WEAPON_FIREGUN:
	 DrawFireBall();
	 break;
	}
}

void MovePulseBullet(int nbul)	// Move the bullet for gun 2
{
	float xf,zf;

	bul[nbul].x+=bul[nbul].dx-(sin(rotation)/8);
	bul[nbul].y+=bul[nbul].dy-(sin(rotation)/8);
	bul[nbul].z+=bul[nbul].dz-(sin(rotation)/8);

	// Check Collision with Terrain
	xf=bul[nbul].x/SIZE;
	zf=bul[nbul].z/SIZE;
	if( xf<0 || xf>MAPX || zf<0 || zf>MAPZ )
	{
		bul[nbul].active=-1;
		return;
	}
}

void MoveRedBullet(int nbul)	// Move the bullet for gun 1 
{
	float xf,zf;

	bul[nbul].x+=bul[nbul].dx;
	bul[nbul].y+=bul[nbul].dy;
	bul[nbul].z+=bul[nbul].dz;

	// Check Collision with Terrain
	xf=bul[nbul].x/SIZE;
	zf=bul[nbul].z/SIZE;


	if( xf<0 || xf>MAPX || zf<0 || zf>MAPZ )
	{
		bul[nbul].active=-1;
		return;
	}

	if(bul[nbul].y<0)
	{
		bul[nbul].active=-1;
		InitParticles1(nbul);
		return;
	}
	
	if( map[(int)xf][(int)zf].celltype!=0 && bul[nbul].y<WALLHEIGHT)
	{
		bul[nbul].active=-1;
		InitParticles1(nbul);
		return;
	}

	if(bul[nbul].active>0)
		bul[nbul].active--;
}

void MovePlasmaBullet(int nbul)	// Move the bullet for gun 3 
{
	float xf,zf;

	bul[nbul].x+=bul[nbul].dx;//*sin(rotation*4);
	bul[nbul].y+=bul[nbul].dy;//*sin(rotation*4);
	bul[nbul].z+=bul[nbul].dz;//*sin(rotation*4);

	// Check Collision with Terrain
	xf=bul[nbul].x/SIZE;
	zf=bul[nbul].z/SIZE;

	if( xf<0 || xf>MAPX || zf<0 || zf>MAPZ || bul[nbul].z < 0 )
	{
		bul[nbul].active=-1;
		return;
	}

	if(bul[nbul].y<0)
	{
		bul[nbul].dy=-bul[nbul].dy;
		bul[nbul].active-=20;
		return;
	}
	
	if( map[(int)xf][(int)zf].celltype!=0 && bul[nbul].y<WALLHEIGHT)
	{
		bul[nbul].dx=-bul[nbul].dx;
		bul[nbul].dz=-bul[nbul].dz;
		bul[nbul].active-=20;
		return;
	}
}

void MoveFireBullet(int nbul)	// Move the bullet for gun 4
{
	float xf,zf;

	bul[nbul].x+=bul[nbul].dx;
	bul[nbul].y+=bul[nbul].dy;
	bul[nbul].z+=bul[nbul].dz;


	// Check Collision with Terrain
	xf=bul[nbul].x/SIZE;
	zf=bul[nbul].z/SIZE;
	if( xf<0 || xf>MAPX || zf<0 || zf>MAPZ )
	{
		bul[nbul].active=-1;
		return;
	}

    if(bul[nbul].y>0)
		bul[nbul].dy-=0.01f;
	else
	{
		DrawFireBlast();
		bul[nbul].active=-1;
		return;
	}

	if( map[(int)xf][(int)zf].celltype!=0 && bul[nbul].y<WALLHEIGHT)
	{
		DrawFireBlast();
		bul[nbul].active=-1;
		return;
	}

	if(bul[nbul].active>0)
		bul[nbul].active--;
}


void DrawBullet(void)	// Draws the bullets
{
	for(int t=0;t<MAXBULLET;t++)
	{
		if(bul[t].active>0)
		{
			if(!Visible(Visuale.xdest,Visuale.zdest,bul[t].x,bul[t].z))
				   continue;

			switch(Io.guntype)
			{
			case WEAPON_PULSER:
				DrawSprite(bul[t].x,bul[t].y,bul[t].z,(sin(rotation*4))/2,0,1,1,Io.x,Io.y,Io.z,8);	// Draw the Bullets
				MovePulseBullet(t);
				break;
			case WEAPON_RIFLE:
				DrawSprite(bul[t].x,bul[t].y,bul[t].z,0.15f,1,1,1,Io.x,Io.y,Io.z,9);	// Draw the Bullets
				MoveRedBullet(t);
				break;
			case WEAPON_PLASMAGUN:
				InitParticles1(t);
				MovePlasmaBullet(t);
				break;
			case WEAPON_FIREGUN:
				InitParticles2(t);
				MoveFireBullet(t);
				break;
			}
		}
	}
}

//#################################
//--------------- enemy functions #
//#################################

bool CheckEnemy(void)	// False if the are no enemy live
{
	for(int t=0;t<var.nenemy;t++)
	{
		if(enemy[t].active==1)
			return true;
	}
	return false;
}

void MoveEnemy(void)	// !!
{
	int t,j,n;
	float dist,dmin,dx,dy,xp,yp;
	int xa,ya,xb,yb;
	bool vedi;
	CCC temp;

	// Enemy Direction
	for(n=0;n<var.nenemy;n++)
	{
		enemy[n].y=2;

		xa=(int)Io.x;
		ya=(int)Io.z;
		xb=(int)enemy[n].x;
		yb=(int)enemy[n].z;

		dmin=1000;
		j=0;
		for(t=0;t<8;t++)
		{
			dist=sqrt( ((xb - (xa + dxs[t])) * (xb - (xa + dxs[t]))) + (yb - (ya + dys[t])) * (yb - (ya + dys[t])) );
			cdist[t].dist=dist;
			cdist[t].pos=t;
		}

		for(t=0;t<8;t++)
		{
			for(j=0;j<8;j++)
			{
				if( cdist[j].dist>cdist[t].dist )
				{
					temp=cdist[j];
					cdist[j]=cdist[t];
					cdist[t]=temp;
				}
			}
		}
	
		for(t=0;t<8;t++)
		{
			xa=(int)(enemy[n].x/SIZE-(float)dxs[cdist[t].pos]/10);
			ya=(int)(enemy[n].z/SIZE-(float)dys[cdist[t].pos]/10);
		
			if( xa>=0 && xa<MAPX && ya>=0 && ya<MAPZ )
			{
				if( map[xa][ya].celltype==EMPTYCELL )
				{
					enemy[n].x-=(float)dxs[cdist[t].pos]/10;
					enemy[n].z-=(float)dys[cdist[t].pos]/10;
					t=9;
					break;
				}
				else
				{
					enemy[n].x+=(float)dxs[cdist[t].pos]/10;
					enemy[n].z+=(float)dys[cdist[t].pos]/10;
					t=9;
					break;
				}
			}
		}

		// The enemy can see me?
		dist=Contact(Io.x,Io.y,Io.z,enemy[n].x, enemy[n].y, enemy[n].z);
		if(dist>0)
		{
			dx=(Io.x-enemy[n].x)/dist;
			dy=(Io.z-enemy[n].z)/dist;
			xp=enemy[n].x;
			yp=enemy[n].z;
			vedi=true;
			for(t=0;t<dist;t++)
			{
				xa=(int)(xp/SIZE);
				ya=(int)(yp/SIZE);
				if(xa>=0 && xa<MAPX && ya>=0 && ya<MAPZ)
				{
					if( map[xa][ya].celltype!=0)
					{
						vedi=false;
						break;
					}
					xp+=dx;
					yp+=dy;
				}
				else
				{
					vedi=false;
					break;
				}
			}
		}
		// Vedi is true if the enemy can see me.
		if(vedi==true && enemy[n].sound==false)
		{
			enemy[0].sound=true;
			//PlaySound("Ghost.wav",NULL,SND_FILENAME|SND_ASYNC);
		}

		for(t=0;t<MAXBULLET;t++)
		{
			if(Contact(enemy[n].x,enemy[n].y,enemy[n].z,bul[t].x,bul[t].y,bul[t].z) < 1.5f)
			{
				bul[t].active=-1;

				if(Io.guntype==WEAPON_PULSER)
				  enemy[n].health-=10;
				if(Io.guntype==WEAPON_PLASMAGUN)
				  enemy[n].health-=50;
				if(Io.guntype==WEAPON_FIREGUN)
				  enemy[n].health-=100;
				else
				  enemy[n].health-=5;

				if((enemy[n].health<=0)&&(enemy[n].active==true))
				{
					InitExplosion(enemy[n].x,enemy[n].y,enemy[n].z);
					enemy[n].active=false;
				}

				for(t=0;t<8;t++)
				{
			     xa=(int)(enemy[n].x/SIZE-(float)dxs[cdist[t].pos]/10);
			     ya=(int)(enemy[n].z/SIZE-(float)dys[cdist[t].pos]/10);
		
			    if( xa>=0 && xa<MAPX && ya>=0 && ya<MAPZ )
				{
				 if( map[xa][ya].celltype==EMPTYCELL )
				 {
					enemy[n].x+=(float)dxs[cdist[t].pos]/2;
					enemy[n].z+=(float)dys[cdist[t].pos]/2;
					t=9;
					break;
				 }
				 if( map[xa][ya].celltype!=EMPTYCELL )
				 {
					enemy[n].x-=(float)dxs[cdist[t].pos]/50;
					enemy[n].z-=(float)dys[cdist[t].pos]/50;
					t=9;
					break;
				 }
				}
				}
			}

		}

	}
}

void DrawEnemy(void)	// !!
{
	int t;

	glColor3f(1,1,1);
	for(t=0;t<var.nenemy;t++)
	{
		if(enemy[t].active==true)
		{
		  if(!Visible(Visuale.xdest,Visuale.zdest,enemy[t].x,enemy[t].z))
			continue;

		  glPushMatrix();
			
		  glEnable(GL_CULL_FACE);
		  glCullFace(GL_BACK);

		  glTranslatef(enemy[t].x,enemy[t].y,enemy[t].z);

		  if(var.tenemy==ENEMY_GHOST)
		  {
			glScalef(0.03f, 0.03f, 0.03f);

			glColor4f(1,1,1,1.0f);

			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);

			glBindTexture(GL_TEXTURE_2D, texture[6]);
       
		    Bauul.Animate(Bauul.stateStart, Bauul.stateEnd, 0.08f);
		  }

		  if(var.tenemy==ENEMY_FIRE)
		  {
			 glScalef(0.04f, 0.04f, 0.04f);
			 glColor4f(0.8f,0.5f,0.3f,1.0f);

	         glBindTexture( GL_TEXTURE_2D, texture[5] );

			 Bauul.Animate(Bauul.stateStart, Bauul.stateEnd, 0.08f);
		  }

          glDisable(GL_BLEND);
		  glDisable(GL_CULL_FACE);

		  glPopMatrix();
		}
	}
}

void DrawEnemyEffect()
{
	for(int t=0;t<var.nenemy;t++)
	{
		if(enemy[t].active==true)
		{

	     if(var.tenemy==ENEMY_GHOST)
		 {
			glPushMatrix();
			
			 DrawSprite(enemy[t].x, enemy[t].y-0.5f, enemy[t].z,2.0f,0.1f,0.1f,0.1f,Io.x,Io.y,Io.z,8);
			
			glPopMatrix();
		 }
         else
		 {
			glPushMatrix();
			
			 for(int i=0; i<5; i++)
			  DrawSprite(enemy[t].x+(sin(rotation*(i+1))), (enemy[t].y)+(i/5), enemy[t].z+(cos(rotation*(i+1))),0.5f,0.6f,0.3f,0.1f,Io.x,Io.y,Io.z,8);
			
			glPopMatrix();
		 }
		}
	}
}

//#################################
//--------------- weapon drawings #
//#################################

void DrawWeapon(void)	// Draws the gun
{
	float dx,dz;

	// Draw Rifle
	dx=cos( (Visuale.anx-30)*3.141/180 );
	dz=-sin( (Visuale.anx-30)*3.141/180 );
	dx=dx*2.5f;
	dz=dz*2.5f;
	
	glPushMatrix();
	glTranslatef(Io.x+dx,Io.y-1,Io.z+dz);
	glRotatef(Visuale.anx-90,0,1,0);
	glRotatef(-Visuale.any,1,0,0);
	glCallList(rifle_list);
	glPopMatrix();
}

//#################################
//-------------- map functions    #
//#################################

void Portal(void)	// Transport in a random portal of the map
{
	int x,z;

	for(z=0;z<var.mapz;z++)
	{
		for(x=0;x<var.mapx;x++)
		{
			if(map[x][z].celltype==3 && rand()%4==1)
			{
				if(map[x-1][z].celltype==0)
				{
					Io.x=(float)(x-1)*SIZE;
					Io.z=(float)z*SIZE;
				}
				else
				{
					if(map[x+1][z].celltype==0)
					{
						Io.x=(float)(x+1)*SIZE;
						Io.z=(float)z*SIZE;
					}
					else
					{
						if(map[x][z-1].celltype==0)
						{
							Io.x=(float)x*SIZE;
							Io.z=(float)(z-1)*SIZE;
						}
						else
						{
							Io.x=(float)x*SIZE;
							Io.z=(float)(z+1)*SIZE;
						}
					}
				}
			}
		}
	}
}

int LoadMap(void)	// !!
{
	char s[80];
	FILE *ptr;
	int lines=0;
	int x,z;
	int k,i;

	strcpy(s,nomemap[var.mapnumber]);
	ptr=fopen(s,"r");
	if(ptr==NULL)
		return(FALSE);

	Io.x=15;
	Io.y=3;
	Io.z=15;
	Io.angolo=0;
	Io.vx=0;
	Io.vy=0;
	Io.vz=0;

	// Map size
	fgets(s,80,ptr);
	var.mapx=atoi(s);	// X
	fgets(s,80,ptr);
	var.mapz=atoi(s);	// Z
	// Enemy Number, Init Enemy Health
	fgets(s,80,ptr);
	var.nenemy=atoi(s);
	fgets(s,80,ptr);
	var.henemy=atoi(s);
	
	if(var.henemy<=150)
		 var.tenemy=ENEMY_GHOST;
	else
         var.tenemy=ENEMY_FIRE;

	//Init Number Guns
	fgets(s,80,ptr);
	//Io.nguns=atoi(s);
	Io.guntype=WEAPON_RIFLE;
	// Random Map?
	fgets(s,80,ptr);
	var.randommap=atoi(s);
	if((var.randommap==1)||(var.mapnumber>10))	// Yes, Generate a Random Map
	{
		for(z=0;z<var.mapz;z++)
		{
			for(x=0;x<var.mapx;x++)
			{
				if(rand()%20==1)
				{
					switch(rand()%10)
					{
					case 0:
						map[x][z].celltype=FULLCELL;
						break;
					case 1:
						map[x][z].celltype=FULLCELL;
						break;
					case 2:
						map[x][z].celltype=FULLCELL;
						break;
					case 3:
						map[x][z].celltype=2;
						break;
					case 4:
						map[x][z].celltype=FULLCELL;
						break;
					case 5:
						map[x][z].celltype=FULLCELL;
						break;
					case 6:
						map[x][z].celltype=FULLCELL;
						break;
					case 7:
						map[x][z].celltype=3;
						break;
					case 8:
						map[x][z].celltype=FULLCELL;
						break;
					case 9:
						map[x][z].celltype=FULLCELL;
						break;
					}
				}
				else
					map[x][z].celltype=EMPTYCELL;
				map[x][0].celltype=1;
				map[x][var.mapz-1].celltype=1;
			}
			map[0][z].celltype=1;
			map[var.mapx-1][z].celltype=1;
		}
	}
	else
	{
		lines=var.mapz;	// Load a Map
		x=0;
		z=0;
		do
		{
			fgets(s,100,ptr);
			for(x=0;x<var.mapx;x++)
			{
				k=(int)s[x]-48;
				map[x][z].celltype=k;
			}
			z++;
			lines--;
		}while(lines>0);
	}

	fclose(ptr);

	// Init Enemy
	for(k=0;k<var.nenemy;k++)
	{
		do
		{
			x=rand()%var.mapx;
			z=rand()%var.mapz;
		}while(map[x][z].celltype!=0);
		
		enemy[k].x=(float)x*SIZE;
		enemy[k].y=WALLHEIGHT;
		enemy[k].z=(float)z*SIZE;
		enemy[k].active=true;
		enemy[k].health=var.henemy;
		enemy[k].vel=(float)(rand()%100)/1000;
		enemy[k].sound=false;
	}
	
	var.show_console=false;
	var.panel=false;
	var.changelevel=false;

	// Bullet Explosion
	for(i=0;i<MAXBULLET;i++)
		par[i].active=-1;

	return(TRUE);
}

void DrawMap()
{
	int xi,zi;
	float xf,zf;
	int ntex;

	glColor3f(1,1,1);

	for(zi=0;zi<var.mapz;zi++)
	{
		zf=SIZE*(float)zi;
		for(xi=0;xi<var.mapx;xi++)
		{
			xf=SIZE*(float)xi;

			if(map[xi][zi].celltype==EMPTYCELL)
			{
			  //floor is drawn elsewhere :-D
			}
			else
			{
			   if((set.cull_map==true)||(set.ppl=true))
			   {
			    if(!Contact(Io.x,Io.y,Io.z,(xf+SIZE)/2,WALLHEIGHT/2,(zf+SIZE)/2) > set.cull_dist/((int)(set.ppl+1)))
				   continue;
			   }

			   if(!Visible(Visuale.xdest,Visuale.zdest,xf+SIZE/2,zf+SIZE/2))
				   continue;

			   if(set.fogcoords==true)
				   glEnable(GL_FOG);

			   if(set.ppl==true) //1st pass renders the lightmap
			   {
				glBlendFunc(GL_ONE, GL_ONE);
	            glEnable(GL_BLEND);

	            glActiveTextureARB(GL_TEXTURE0_ARB);
	            glEnable(GL_TEXTURE_2D);
	            glBindTexture(GL_TEXTURE_2D, texture[14]);

	            glActiveTextureARB(GL_TEXTURE1_ARB);
	            glEnable(GL_TEXTURE_2D);
	            glBindTexture(GL_TEXTURE_2D, texture[15]);

				glCallList(setup_register_combs);

				glBegin(GL_QUADS);

				 ProcessVertex_PPL(xf,0,zf);
				 ProcessVertex_PPL(xf,WALLHEIGHT,zf);
				 ProcessVertex_PPL(xf+SIZE,WALLHEIGHT,zf);
				 ProcessVertex_PPL(xf+SIZE,0,zf);

				 ProcessVertex_PPL(xf,0,zf);
				 ProcessVertex_PPL(xf,WALLHEIGHT,zf);
				 ProcessVertex_PPL(xf,WALLHEIGHT,zf+SIZE);
				 ProcessVertex_PPL(xf,0,zf+SIZE);

				 ProcessVertex_PPL(xf,0,zf+SIZE);
				 ProcessVertex_PPL(xf,WALLHEIGHT,zf+SIZE);
				 ProcessVertex_PPL(xf+SIZE,WALLHEIGHT,zf+SIZE);
				 ProcessVertex_PPL(xf+SIZE,0,zf+SIZE);

				 ProcessVertex_PPL(xf+SIZE,0,zf);
				 ProcessVertex_PPL(xf+SIZE,WALLHEIGHT,zf);
				 ProcessVertex_PPL(xf+SIZE,WALLHEIGHT,zf+SIZE);
				 ProcessVertex_PPL(xf+SIZE,0,zf+SIZE);

				glEnd();

            	glActiveTextureARB(GL_TEXTURE1_ARB);
            	glDisable(GL_TEXTURE_2D);

            	glDisable(GL_REGISTER_COMBINERS_NV);

				glBlendFunc(GL_DST_COLOR, GL_ZERO);

	            glActiveTextureARB(GL_TEXTURE0_ARB);
				glEnable(GL_TEXTURE_2D);

			   }

			   //2nd pass (only one without ppl ;) renders the single-textured level
				ntex=map[xi][zi].celltype;

				// 4 Walls
				glBindTexture( GL_TEXTURE_2D, texture[ntex+1] );
				glBegin(GL_QUADS);
				 
				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 1,0 );
				 glVertex3f(xf,0,zf);

				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 1,1 );
				 glVertex3f(xf,WALLHEIGHT,zf);
				 
				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 0,1 );
				 glVertex3f(xf+SIZE,WALLHEIGHT,zf);
				 
				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 0,0 );
				 glVertex3f(xf+SIZE,0,zf);


				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 1,0 );
				 glVertex3f(xf,0,zf);
				 
				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 1,1 );
				 glVertex3f(xf,WALLHEIGHT,zf);
				 
				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 0,1 );
				 glVertex3f(xf,WALLHEIGHT,zf+SIZE);
				
				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 0,0 );
				 glVertex3f(xf,0,zf+SIZE);



				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 1,0 );
				 glVertex3f(xf,0,zf+SIZE);

				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 1,1 );
				 glVertex3f(xf,WALLHEIGHT,zf+SIZE);

				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 0,1 );
				 glVertex3f(xf+SIZE,WALLHEIGHT,zf+SIZE);

				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 0,0 );
				 glVertex3f(xf+SIZE,0,zf+SIZE);



				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 1,0 );
				 glVertex3f(xf+SIZE,0,zf);

				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 1,1 );
				 glVertex3f(xf+SIZE,WALLHEIGHT,zf);
				 
				 glFogCoordfEXT(1.0f);
				 glTexCoord2d( 0,1 );
				 glVertex3f(xf+SIZE,WALLHEIGHT,zf+SIZE);

				 glFogCoordfEXT(0.0f);
				 glTexCoord2d( 0,0 );
				 glVertex3f(xf+SIZE,0,zf+SIZE);

				glEnd();

				if(set.fogcoords==true)
				   glDisable(GL_FOG);

			}
		}
	}
}

void DrawFloor()
{
  if((set.floor_mirror==true)&&(set.ppl==false))
  {
    glCallList(stencil_list);

	glCallList(floor_list);

	glEnable(GL_DEPTH_TEST);							// Enable Depth Testing
	glColorMask(1,1,1,1);								// Set Color Mask to TRUE, TRUE, TRUE, TRUE
	glStencilFunc(GL_EQUAL, 1, 1);						// We Draw Only Where The Stencil Is 1
			
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);				// Don't Change The Stencil Buffer
	glPushMatrix();										// Push The Matrix Onto The Stack
		glScalef(1.0f, -1.0f, 1.0f);					// Mirror Y Axis
		DrawMap();
		DrawEnemy();
		DrawEnemyEffect();
	    DrawWeapon();
	glPopMatrix();	
	glDisable(GL_STENCIL_TEST);	

	glEnable(GL_BLEND);									// Enable Blending (Otherwise The Reflected Object Wont Show)
	glColor4f(1.0f, 1.0f, 1.0f, 0.6f);					// Set Color To White With 80% Alpha
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	// Blending Based On Source Alpha And 1 Minus Dest Alpha

	glBindTexture(GL_TEXTURE_2D,texture[5]);

	glCallList(floor_list);
  }
  if(set.ppl==true)
  {
	            if(set.fogcoords==true)
	             glEnable(GL_FOG);

	            glBlendFunc(GL_ONE, GL_ONE);
	            glEnable(GL_BLEND);

	            glActiveTextureARB(GL_TEXTURE0_ARB);
	            glEnable(GL_TEXTURE_2D);
	            glBindTexture(GL_TEXTURE_2D, texture[14]);

	            glActiveTextureARB(GL_TEXTURE1_ARB);
	            glEnable(GL_TEXTURE_2D);
	            glBindTexture(GL_TEXTURE_2D, texture[15]);

				glCallList(setup_register_combs);

				glBegin(GL_QUADS);
				 ProcessVertex_PPL(0,0,0);
				 ProcessVertex_PPL(var.mapx*SIZE,0,0);
				 ProcessVertex_PPL(var.mapx*SIZE,0,var.mapz*SIZE);
				 ProcessVertex_PPL(0,0,var.mapz*SIZE);
				glEnd();

            	glActiveTextureARB(GL_TEXTURE1_ARB);
            	glDisable(GL_TEXTURE_2D);

            	glDisable(GL_REGISTER_COMBINERS_NV);

				glBlendFunc(GL_DST_COLOR, GL_ZERO);

	            glActiveTextureARB(GL_TEXTURE0_ARB);
				glBindTexture(GL_TEXTURE_2D,texture[5]);

	            glCallList(floor_list);

  }
  else
  {
	            if(set.fogcoords==true)
	             glEnable(GL_FOG);

				glBindTexture(GL_TEXTURE_2D,texture[5]);
	            glCallList(floor_list);
  }

  if(set.fogcoords==true)
	 glDisable(GL_FOG);
}

//#################################
//------------------- initiations #
//#################################

void Init(void)
{	
	var.mapnumber=0;

	var.explosion=false;
	var.panel=false;

	var.enemies_alive=true;

	if(LoadMap()==FALSE)
		exit(1);
	
	// Bullet Explosion
	for(int j=0;j<MAXBULLET;j++)
		par[j].active=-1;

	// Dxs & Dys
	dxs[0]=-1;
	dys[0]=-1;
	dxs[1]=0;
	dys[1]=-1;
	dxs[2]=1;
	dys[2]=-1;
	dxs[3]=-1;
	dys[3]=0;
	dxs[4]=1;
	dys[4]=0;
	dxs[5]=-1;
	dys[5]=1;
	dxs[6]=0;
	dys[6]=1;
	dxs[7]=1;
	dys[7]=1;

	Visuale.dx=0;
	Visuale.dy=0;
	Visuale.dz=0;
	Visuale.anx=0;
	Visuale.any=0;
	Visuale.mousex=0;
	Visuale.mousey=0;
	Visuale.oldmousex=0;
	Visuale.oldmousey=0;

	/* Initial Position
	Io.x=15;
	Io.y=3;
	Io.z=15;
	Io.angolo=0;
	Io.vx=0;
	Io.vy=0;
	Io.vz=0;
	*/

	Io.active=true;
	Io.health=100;
	Io.stamina=100;
	Io.strength=50;
	Io.walkmode=PL_WALK;
	Io.nguns=4; //player will have to collect enhancement chips to increment this =)
}

//#################################
//----------------- Display Lists #
//#################################


void GenerateWeaponLists(void)
{
	rifle_list=glGenLists(1);
	glNewList(rifle_list,GL_COMPILE);
	  glDisable(GL_BLEND);
	  glEnable(GL_CULL_FACE);
      glCullFace(GL_BACK);
      glColor3f(1.0f,1.0f,1.5f);
	  
	  glActiveTextureARB(GL_TEXTURE0_ARB);
	   glBindTexture( GL_TEXTURE_2D, texture[rifle.tex_num] );

      glActiveTextureARB(GL_TEXTURE1_ARB);        //doesn't look much different (first to remove if performance problems occur)
	   glEnable(GL_TEXTURE_2D);
	   glEnable(GL_TEXTURE_GEN_S) ;
       glEnable(GL_TEXTURE_GEN_T) ;
       glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT) ;
       glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
	   glBindTexture( GL_TEXTURE_2D, texture[11] );

	  rifle.Render();
	  glDisable(GL_CULL_FACE);
	  glDisable(GL_TEXTURE_GEN_S) ;
      glDisable(GL_TEXTURE_GEN_T) ;

	  glActiveTextureARB(GL_TEXTURE1_ARB);
	  glDisable(GL_TEXTURE_2D);

	  glActiveTextureARB(GL_TEXTURE0_ARB);
	glEndList();
}

void GenerateGFXLists(void)
{
	ortho_list=glGenLists(1);
	glNewList(ortho_list,GL_COMPILE);
	  glMatrixMode(GL_PROJECTION);						
	  glPushMatrix();									
	  glLoadIdentity();								
	  glOrtho( 0, set.width,  set.height, 0, 0, 1 );	
	  glMatrixMode(GL_MODELVIEW);								
	  glLoadIdentity();
	glEndList();

	perspective_list=ortho_list+1;
	glNewList(perspective_list,GL_COMPILE);
	 glMatrixMode( GL_PROJECTION );							
     glPopMatrix();											
     glMatrixMode( GL_MODELVIEW );
	glEndList();

	stencil_list=perspective_list+1;
	glNewList(stencil_list, GL_COMPILE);
	 glColorMask(0,0,0,0);							
	 glEnable(GL_STENCIL_TEST);						
	 glStencilFunc(GL_ALWAYS, 1, 1);					
	 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	 glDisable(GL_DEPTH_TEST);	
	glEndList();
	
	setup_register_combs=stencil_list+1;
	glNewList(setup_register_combs, GL_COMPILE);
	  glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);

	  glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, (float *) &Light_Color);

	  glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
	  glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
	  
	  glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

	  glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	  glFinalCombinerInputNV(GL_VARIABLE_G_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);

	  glEnable(GL_REGISTER_COMBINERS_NV);
	glEndList();
}


void GenerateLists(void)
{
	GenerateWeaponLists();
	GenerateGFXLists();

	floor_list=glGenLists(1);
	glNewList(floor_list, GL_COMPILE);
	 glBegin(GL_QUADS);
	  glTexCoord2d( 0,0 );           glVertex3f(0,0,0);
	  glTexCoord2d( SIZE*2,0 );      glVertex3f(var.mapx*SIZE,0,0);
	  glTexCoord2d( SIZE*2,SIZE*2 ); glVertex3f(var.mapx*SIZE,0,var.mapz*SIZE);
	  glTexCoord2d( 0,SIZE*2 );      glVertex3f(0,0,var.mapz*SIZE);
	 glEnd();
	glEndList();
}



//#################################
//--------------- texture loading #
//################################# 

AUX_RGBImageRec *LoadBMP(const char *Filename)
{
	FILE *File=NULL;

	if (!Filename)
	{
		return NULL;
	}

	File=fopen(Filename,"r");

	if (File)
	{
		fclose(File);
		return auxDIBImageLoad(Filename);
	}

	return NULL;
}

int LoadBMPTexture(char name[80], int num)
{
	int Status=FALSE;

	AUX_RGBImageRec *TextureImage[1];

	memset(TextureImage,0,sizeof(void *)*1);

	if (TextureImage[0]=LoadBMP(name))
	{
		Status=TRUE;

		glGenTextures(1, &texture[num]);

		glBindTexture(GL_TEXTURE_2D, texture[num]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
		
		if(set.s3tc==true)
		{
  	     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
		}
		else
		 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
	 }

	if (TextureImage[0])
	{
		if (TextureImage[0]->data)
		{
			free(TextureImage[0]->data);
		}

		free(TextureImage[0]);
	}

	return Status;
}
				
void DecodeJPG(jpeg_decompress_struct* cinfo, tImageJPG *pImageData)
{
	jpeg_read_header(cinfo, TRUE);
    jpeg_start_decompress(cinfo);

	pImageData->rowSpan = cinfo->image_width * cinfo->num_components;
	pImageData->sizeX   = cinfo->image_width;
	pImageData->sizeY   = cinfo->image_height;
	
	pImageData->data = new unsigned char[pImageData->rowSpan * pImageData->sizeY];
		
	unsigned char** rowPtr = new unsigned char*[pImageData->sizeY];

	for (int i = 0; i < pImageData->sizeY; i++)
		rowPtr[i] = &(pImageData->data[i*pImageData->rowSpan]);

	int rowsRead = 0;

	while (cinfo->output_scanline < cinfo->output_height) 
		rowsRead += jpeg_read_scanlines(cinfo, &rowPtr[rowsRead], cinfo->output_height - rowsRead);
	
	delete [] rowPtr;

	jpeg_finish_decompress(cinfo);
}

tImageJPG *LoadJPG(const char *filename)
{
	struct jpeg_decompress_struct cinfo;
	tImageJPG *pImageData = NULL;
	FILE *pFile;
	
	if((pFile = fopen(filename, "rb")) == NULL) 
	{
		MessageBox(hWnd, "Unable to load JPG File!", "Error", MB_OK);
		return NULL;
	}

	jpeg_error_mgr jerr;

	cinfo.err = jpeg_std_error(&jerr);
	
	jpeg_create_decompress(&cinfo);	
	jpeg_stdio_src(&cinfo, pFile);

	pImageData = (tImageJPG*)malloc(sizeof(tImageJPG));

	DecodeJPG(&cinfo, pImageData);
	
	jpeg_destroy_decompress(&cinfo);

	fclose(pFile);

	return pImageData;
}

int CreateJPGTexture(LPSTR strFileName, int textureID)
{
	if(!strFileName)									// Return from the function if no file name was passed in
		return false;
		
	tImageJPG *pImage = LoadJPG(strFileName);			// Load the image and store the data

	if(pImage == NULL)									// If we can't load the file, quit!
		return false;

	glGenTextures(1, &texture[textureID]);

	glBindTexture(GL_TEXTURE_2D, texture[textureID]);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);

	if(set.s3tc)
	 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, pImage->sizeX, pImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pImage->data);
    else
	 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, pImage->sizeX, pImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pImage->data);	

	if (pImage)										// If we loaded the image
	{
		if (pImage->data)							// If there is texture data
		{
			free(pImage->data);						// Free the texture data, we don't need it anymore
		}

		free(pImage);								// Free the image structure
	}
	return true;
}

int CreateResTexture(int image,int number)
{
	HBITMAP h;
	

	if (GetModuleHandle(0))
		h = (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(image),IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
	else
		h = (HBITMAP)LoadImage(0, MAKEINTRESOURCE(image), IMAGE_BITMAP, 0, 0,LR_LOADFROMFILE | LR_CREATEDIBSECTION);

	if (h) 
	{
		DIBSECTION ds;
		GetObject(h, sizeof(ds), &ds);
		int sz=(ds.dsBmih.biWidth<ds.dsBmih.biHeight)?ds.dsBmih.biWidth:ds.dsBmih.biHeight;
		double d=log(sz)/log(2);
		int txbits = (int)floor(d+0.01);
		int txsize = 1 << txbits;
		DeleteObject(h);
		if (GetModuleHandle(0))
			h = (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(image),IMAGE_BITMAP, txsize, txsize, LR_CREATEDIBSECTION);
		else
			h = (HBITMAP)LoadImage(0, MAKEINTRESOURCE(image), IMAGE_BITMAP,txsize, txsize, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
		GetObject(h, sizeof(ds), &ds);

		BITMAPINFOHEADER a = {
			sizeof(BITMAPINFOHEADER),
			txsize,
			-txsize,
			1,
			24,
			BI_RGB,
			3*txsize*txsize,  /* 3 BGR, 4 BGRA */
			0,
			0,
			0,
			0
			};
	
		HDC dc;

		dc=GetDC(NULL);

		unsigned char *texturebis = new unsigned char[a.biSizeImage];
		GetDIBits(dc, h, 0, txsize, texturebis, (BITMAPINFO *)&a,DIB_RGB_COLORS);
		
		glGenTextures(1, &texture[number]); 
		glBindTexture(GL_TEXTURE_2D, texture[number]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, txsize, txsize, GL_RGB, GL_UNSIGNED_BYTE, texturebis);
		DeleteObject(h);
		ReleaseDC(NULL,dc);
		return true;
	}
	else
	   return false;
}

int CreateRenderTexture(UINT textureArray[], int size, int channels, int type, int textureID)										
{
	unsigned int *pTexture = NULL;											

	pTexture = new unsigned int [size * size * channels];
	memset(pTexture, 0, size * size * channels * sizeof(unsigned int));	

	glGenTextures(1, &textureArray[textureID]);								
	glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);					
	
	glTexImage2D(GL_TEXTURE_2D, 0, channels, size, size, 0, type, GL_UNSIGNED_INT, pTexture);						
	
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	delete [] pTexture;	
	return true;
}

int CreateTexture(LPSTR strFileName, int textureID)
{
	if(strstr(strFileName,".bmp"))
	{
		if(!LoadBMPTexture(strFileName, textureID))
			return false;
	}
	if(strstr(strFileName,".jpg"))
	{
		if(!CreateJPGTexture(strFileName, textureID))
			return false;
	}

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	return true;
}

//#################################
//------ main rendering functions #
//#################################

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
{
	if (height==0)
	{
		height=1;
	}

	var.sw=width;
	var.sh=height;

	glViewport(0,0,width,height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,500.0f);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

int InitGL(GLvoid)
{
	GLfloat LightAmbientA[]=	{ 0.0f, 0.3f, 0.4f, 1.0f };
	GLfloat LightDiffuseA[]=		{ 0.8f, 0.8f, 0.8f, 1.0f };
	GLfloat LightSpecularA[]=		{ 0.7f, 0.7f, 0.5f, 1.0f };
	GLfloat LightPositionA[]=	{ 0.0f, 15.0f, 0.0f, 1.0f };

	if (!CreateTexture("Data//white.bmp",0)       ||
		!CreateTexture("Data//Font.bmp",1)        ||
		!CreateTexture("Data//Wall1.bmp",2)       ||
		!CreateTexture("Data//Wall2.bmp",3)       ||
		!CreateTexture("Data//Wall3.bmp",4)       ||
		!CreateTexture("Data//Floor.bmp",5)       ||
		!CreateTexture("Data//Bauul.bmp",6)       ||
        !CreateTexture("Data//weapon.bmp",7)      ||  
		!CreateTexture("Data//Bullet1.bmp",8)     ||
		!CreateTexture("Data//Bullet2.bmp",9)     ||
		!CreateTexture("Data//Panel.jpg",10)      ||
		!CreateTexture("Data//Reflection.bmp",11) ||
		!CreateRenderTexture(texture, 512, 3, GL_RGB, 12) ||
		!CreateTexture("Data//Splatter.bmp",13)   ||
		!CreateTexture("Data//Light1D.jpg",14)    ||
		!CreateTexture("Data//Light2D.jpg",15)    ||
        !CreateTexture("Data//bul3_ico.bmp",16) 
		)
	{
		return FALSE;
	}

	if(!Init_MultiTexturing())
		return false;

	if(!Init_Register_Combiners())
		set.ppl=false;

	BuildFont();

	InitSound();

	Bauul.Load("Data/Bauul.md2");
    Bauul.SetState(RUN);

	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbientA);		// Setup The Ambient Light
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuseA);		// Setup The Diffuse Light
	glLightfv(GL_LIGHT0, GL_POSITION,LightPositionA);	// Position The Light
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecularA);		// Setup The Ambient Light
	glEnable(GL_LIGHT0);

	InitFog();

	Init();

	//Rifle.LoadMS3D("Data/Rifle.ms3d");

	rifle.Init("Data/Rifle.ms3d", 7, 120);
	pulser.Init("Data/Rifle.ms3d", 7, 60);
	PlasmaGun.Init("Data/Rifle.ms3d", 7,20);
	FireGun.Init("Data/Rifle.ms3d", 7, 25);

	GenerateLists();
	
	glActiveTextureARB(GL_TEXTURE0_ARB);
	 glEnable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE1_ARB);
	 glDisable(GL_TEXTURE_2D);

	glScissor(0,0,0,0);

	return TRUE;
}

void Render2Tex()
{
  if( AnimateNextFrame(BLURRATE) )
  {
		glViewport(0, 0, VIEWPORT, VIEWPORT);								
								
		DrawBlur(12);
		
		if(var.tenemy==ENEMY_GHOST)
	     DrawEnemy();

		DrawEnemyEffect();

		DrawBullet();

	    if(Io.guntype==WEAPON_RIFLE)
	     DrawImpactSpray();

		if(Io.guntype==WEAPON_PLASMAGUN) 
		 DrawPlasmaWave();

		glBindTexture(GL_TEXTURE_2D,texture[12]);				
		glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VIEWPORT, VIEWPORT, 0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );			
		glViewport(0, 0, set.width, set.height);	
  }	
}

void DrawGLScene(GLvoid)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  glLoadIdentity();

	gluLookAt(Io.x,Io.y,Io.z,Visuale.xdest,Visuale.ydest,Visuale.zdest,0.0f,1.0f,0.0f);

	if(set.draw_blur==true)
	 Render2Tex();

	DrawMap();
	DrawFloor();

	DrawWeapon();

	DrawBullet();
	DrawParticles();

	if((var.show_console==false)&&(var.panel==false))
	 MoveEnemy();

    DrawEnemy();
	DrawEnemyEffect();

	if(var.explosion==true)
	   DrawExplosion();

	if(set.draw_blur==true)
	 DrawBlur(12);

	DrawHud();

  glFlush();
}

//#################################
//-------------- player functions #
//#################################

void MovePlayer(int dir)
{
   if(var.panel==false)
   {
	float xf,zf;
	float x,z,yc;

	Io.vx=Io.walkmode;
	Io.vz=Io.walkmode;

	if(dir==0)
	{
		yc=Io.y-4;
		zf=Io.z+Visuale.dzorto*5;
		xf=Io.x+Visuale.dxorto*5;
		x=xf/SIZE;
		z=zf/SIZE;

		  if( map[(int)x][(int)z].celltype==EMPTYCELL)
		  {
			Io.z+=(Visuale.dzorto/KVEL)*Io.vz;
			Io.x+=(Visuale.dxorto/KVEL)*Io.vx;
			Visuale.xdest=Io.x+RG*cos(Visuale.anx*3.141/180);
			Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
			Visuale.zdest=Io.z-RG*sin(Visuale.anx*3.141/180);
		  }
		
	}
	if(dir==1)
	{
		yc=Io.y-4;
		zf=Io.z-Visuale.dzorto*5;
		xf=Io.x-Visuale.dxorto*5;
		x=xf/SIZE;
		z=zf/SIZE;
		
			if( map[(int)x][(int)z].celltype==EMPTYCELL)
			{
			 Io.z-=(Visuale.dzorto/KVEL)*Io.vz;
			 Io.x-=(Visuale.dxorto/KVEL)*Io.vx;
			 Visuale.xdest=Io.x+RG*cos(Visuale.anx*3.141/180);
			 Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
			 Visuale.zdest=Io.z-RG*sin(Visuale.anx*3.141/180);
			}
	}
	if(dir==2)
	{
		    yc=Io.y-4;
			zf=Io.z+Visuale.dz*5;
			xf=Io.x+Visuale.dx*5;
			x=xf/SIZE;
			z=zf/SIZE;

			if(map[(int)x][(int)z].celltype==3)
			{
				Portal();
			}
			else
			{
				if( map[(int)x][(int)z].celltype==EMPTYCELL)
				{
							Io.z+=(Visuale.dz/KVEL)*Io.vz;
							Io.x+=(Visuale.dx/KVEL)*Io.vx;
							Visuale.xdest=Io.x+RG*cos(Visuale.anx*3.141/180);
							Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
							Visuale.zdest=Io.z-RG*sin(Visuale.anx*3.141/180);
				}
			}
	}
	if(dir==3)
	{
					yc=Io.y-4;
					zf=Io.z-Visuale.dz*5;
					xf=Io.x-Visuale.dx*5;
					x=xf/SIZE;
					z=zf/SIZE;
					
					if( map[(int)x][(int)z].celltype==EMPTYCELL)
					{
							Io.z-=(Visuale.dz/KVEL)*Io.vz;
							Io.x-=(Visuale.dx/KVEL)*Io.vx;
							Visuale.xdest=Io.x+RG*cos(Visuale.anx*3.141/180);
							Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
							Visuale.zdest=Io.z-RG*sin(Visuale.anx*3.141/180);
					}
	}

	if(Io.walkmode==PL_RUN)
	{
		if(Io.strength <= 0)
		{
			Io.strength=0;
			Io.walkmode=PL_WALK;
			Io.stamina-=5;
		}
		Io.strength--;
	}
   }
}

void MoveView(long mx, long my)
{
	float dx, dy;

			Visuale.mousex=mx;
			Visuale.mousey=my;
	
			dx=Visuale.mousex-Visuale.oldmousex;
			dy=Visuale.mousey-Visuale.oldmousey;
			dx=dx*2;
			dy=dy*2;
			Visuale.oldmousex=Visuale.mousex;
			Visuale.oldmousey=Visuale.mousey;
	
			dx=dx/FATTX;
			dy=dy/FATTY;
			Visuale.anx-=dx;

			if(Visuale.anx>360)
				Visuale.anx-=(float)360;
			if(Visuale.anx<0)
				Visuale.anx+=(float)360;
			Visuale.any+=dy;
			if(Visuale.any>90)
				Visuale.any=90;
			if(Visuale.any<-90)
				Visuale.any=-90;

			Visuale.dx=cos(Visuale.anx*3.141/180);
			Visuale.dz=-sin(Visuale.anx*3.141/180);
			Visuale.dy=0.0f;
			Visuale.dxorto=cos((Visuale.anx+90)*3.141/180);
			Visuale.dzorto=-sin((Visuale.anx+90)*3.141/180);
			Visuale.xdest=Io.x+RG*cos(Visuale.anx*3.141/180);
			Visuale.ydest=Io.y-RG*sin(Visuale.any*3.141/180);
			Visuale.zdest=Io.z-RG*sin(Visuale.anx*3.141/180);
}

void CheckDamage()
{
	for(int n=0;n<=var.nenemy;n++)
	{
	 if(((Contact(Io.x,Io.y,Io.z,enemy[n].x, enemy[n].y, enemy[n].z))<1.5f)&&(enemy[n].active==true)) //contact with a living enemy
	 {
		 DrawIcon(13,1.0f,0.0f,0.0f,0.0f,0.0f,set.width, set.height);
		 BindSound(1);
		 Io.health--;
	 }
	}

	if(Io.stamina<=20)
	{
		DrawIcon(13,sin(rotation),0.0f,0.0f,0.0f,0.0f,set.width, set.height);
		Io.health-=4;
		Io.stamina++;
	}

	if(Io.health<=0)
	{
		Io.active=false;
	}
}

//#################################
//----------- framework functions #
//#################################

GLvoid KillGLWindow(GLvoid)
{
	KillFont();
	glDeleteTextures(17,texture);                       

	if (fullscreen)
	{
		ChangeDisplaySettings(NULL,0);
		ShowCursor(TRUE);
	}

	if (hRC)
	{
		if (!wglMakeCurrent(NULL,NULL))
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;
	}

	if (hDC && !ReleaseDC(hWnd,hDC))
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;
	}

	if (hWnd && !DestroyWindow(hWnd))
	{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;
	}

	if (!UnregisterClass("OpenGL",hInstance))
	{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;
	}
}

BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;
	WNDCLASS	wc;
	DWORD		dwExStyle;
	DWORD		dwStyle;
	RECT		WindowRect;
	WindowRect.left=(long)0;
	WindowRect.right=(long)width;
	WindowRect.top=(long)0;
	WindowRect.bottom=(long)height;

	fullscreen=fullscreenflag;

	hInstance			= GetModuleHandle(NULL);
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;
	wc.hIcon			= LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON1));
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH) (LTGRAY_BRUSH);
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "OpenGL";

	if (!RegisterClass(&wc))
	{
		MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}
	
	if (fullscreen)
	{
		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth	= width;
		dmScreenSettings.dmPelsHeight	= height;
		dmScreenSettings.dmBitsPerPel	= bits;
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				fullscreen=false;
			}
			else
			{
				MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
				return false;
			}
		}
	}

	if (fullscreen)
	{
		dwExStyle=WS_EX_APPWINDOW;
		dwStyle=WS_POPUP;
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle=WS_OVERLAPPEDWINDOW;
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

	if (!(hWnd=CreateWindowEx(	dwExStyle,
								"OpenGL",
								title,
								dwStyle |
								WS_CLIPSIBLINGS |
								WS_CLIPCHILDREN,
								0, 0,
								WindowRect.right-WindowRect.left,
								WindowRect.bottom-WindowRect.top,
								NULL,
								NULL,
								hInstance,
								NULL)))
	{
		KillGLWindow();
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	static	PIXELFORMATDESCRIPTOR pfd=
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW |
		PFD_SUPPORT_OPENGL |
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		bits,
		0, 0, 0, 0, 0, 0,
		0,
		0,
		0,
		0, 0, 0, 0,
		16,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0, 0, 0
	};
	
	if (!(hDC=GetDC(hWnd)))
	{
		KillGLWindow();
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
	{
		KillGLWindow();
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))
	{
		KillGLWindow();
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if (!(hRC=wglCreateContext(hDC)))
	{
		KillGLWindow();
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if(!wglMakeCurrent(hDC,hRC))
	{
		KillGLWindow();
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	ShowWindow(hWnd,SW_SHOW);
	SetForegroundWindow(hWnd);
	SetFocus(hWnd);
	ReSizeGLScene(width, height);

	var.lastTickCount = GetTickCount();

	if (!InitGL())
	{
		KillGLWindow();
		MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}
	return true;
}

LRESULT CALLBACK WndProc(	HWND	hWnd,
							UINT	uMsg,
							WPARAM	wParam,
							LPARAM	lParam)
{
	switch (uMsg)
	{
		case WM_ACTIVATE:
		{
			if (!HIWORD(wParam))
			{
				active=true;
			}
			else
			{
				active=false;
			}

			return 0;
		}

		case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
				return 0;
			}
			break;
		}

		case WM_CLOSE:
		{
			PostQuitMessage(0);
			return 0;
		}

		case WM_KEYDOWN:
		{
			keys[wParam] = true;
			return 0;
		}

		case WM_KEYUP:
		{
			keys[wParam] = false;
			return 0;
		}

		case WM_SIZE:
		{
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
			return 0;
		}

		case WM_LBUTTONDOWN:
		{
			if((var.show_console==false)&&(Io.active==true)&&(var.panel==false))
				 shoot=true;

			return 0;	
		}

		case WM_RBUTTONDOWN:
		{
			return 0;
		}

		case WM_MOUSEMOVE:
		{
			if((var.show_console==false)&&(Io.active==true)&&(var.panel==false))
			{
			POINT p;

			mx=LOWORD(lParam);
			my=HIWORD(lParam);

			GetCursorPos(&p);
			if(p.x<50)
			{
				p.x=var.sw-50;
				Visuale.oldmousex=p.x;
				mx=p.x;
				SetCursorPos(p.x,p.y);
			}
			if(p.x>var.sw-50)
			{
				p.x=50;
				Visuale.oldmousex=10;
				mx=p.x;
				SetCursorPos(p.x,p.y);
			}
			if(p.y<100)
			{
				p.y=var.sh-100;
				Visuale.oldmousey=p.y;
				my=p.y;
				SetCursorPos(p.x,p.y);
			}
			if(p.y>var.sh-100)
			{
				p.y=101;
				Visuale.oldmousey=p.y;
				my=p.y;
				SetCursorPos(p.x,p.y);
			}

			return 0;
			}
		}
	}
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

void Update (DWORD milliseconds)								// Perform Motion Updates Here
{
	    if((float)(milliseconds) > 5)
		{
	           if (!active || keys[VK_ESCAPE])
				done=true;

			   DrawGLScene();

	           if(Io.active==true)
			   {
				CheckDamage();

				if (keys[VK_LEFT])
				{
					MovePlayer(0);
				}
				if (keys[VK_RIGHT])
				{
					MovePlayer(1);
				}
				if (keys[VK_UP])
				{
				    MovePlayer(2);
				}
				if (keys[VK_DOWN])
				{
					MovePlayer(3);
				}
				if(keys[VK_SHIFT])
				{
					if(Io.walkmode==PL_RUN)
					 Io.walkmode=PL_WALK;
					else
					 Io.walkmode=PL_RUN;

					keys[VK_SHIFT]=false;
				}
			   }

				if(keys['P'])
				{
					var.panel=!var.panel;

					keys['P']=false;
				}
				
				if (keys[VK_F1])
				{
					keys[VK_F1]=false;
					KillGLWindow();
					fullscreen=!fullscreen;
					if (!CreateGLWindow("GL Shooter",set.width,set.height,set.depth,fullscreen))
					{ 
		             return;
					}
				}
				if (keys[VK_F4])
				{
					keys[VK_F4]=false;
					ShowConsole();
				}
				if (keys['1'])
				{
					keys['1']=false;
			
					if(Io.nguns>0)
					 Io.guntype=WEAPON_RIFLE;
				}
				if (keys['2'])
				{
					keys['2']=false;

					if(Io.nguns>1)
					 Io.guntype=WEAPON_PULSER;
				}
				if (keys['3'])
				{
					keys['3']=false;

					if(Io.nguns>2)
						Io.guntype=WEAPON_PLASMAGUN;
				}
				if (keys['4'])
				{
					keys['4']=false;

					if(Io.nguns>3)
						Io.guntype=WEAPON_FIREGUN;
				}

				MoveView(mx,my);

				if((Io.walkmode==PL_WALK)&&(Io.strength <= Io.stamina))
				{
		         Io.strength++;
				}

				Light_X=Io.x;
				Light_Y=Io.y;
				Light_Z=Io.z;

				Bauul.viewer_x=Io.x;
				Bauul.viewer_y=Io.y;
				Bauul.viewer_z=Io.z;

				var.enemies_alive=CheckEnemy();

				if(shoot==true)
				{
				    shoot=false;

					for(int k=0;k<MAXBULLET;k++)
						bul[k].active=false;

					switch(Io.guntype)
					{
			         case WEAPON_RIFLE:
					   if(rifle.ammo>0)
					   {
				        InitBullet();
						rifle.ammo--;
					   }
				      break;
			         case WEAPON_PULSER:
						if(pulser.ammo>0)
						{
				         InitBullet();
						 pulser.ammo--;
						}
				      break;
			         case WEAPON_PLASMAGUN:
				        if(PlasmaGun.ammo>0)
						{
				         InitBullet();
						 PlasmaGun.ammo--;
						}
					   break;
					 case WEAPON_FIREGUN:
				        if(FireGun.ammo>0)
						{
				         InitBullet();
						 FireGun.ammo--;
						}
				       break;
					}

				}

				if(var.enemies_alive==false)
				{
		         var.explosion=false;
		         var.mapnumber++;

			     LoadMap();
			     var.panel=true;
				}

				rotation+=((float)milliseconds)/200;
		}
}

int WINAPI WinMain(	HINSTANCE	hInstance,
					HINSTANCE	hPrevInstance,
					LPSTR		lpCmdLine,
					int			nCmdShow)
{
	MSG		msg;

	LoadIni();

	fullscreen=set.fullscreen;

	if (!CreateGLWindow("GL Shooter",set.width,set.height,set.depth,fullscreen))
	{
		return 0;
	}

	while(!done)
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if (msg.message==WM_QUIT)
			{
				done=true;
			}
			else
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{
			var.tickCount = GetTickCount();				// Get The Tick Count
			Update(var.tickCount - var.lastTickCount);	// Update The Counter
			var.lastTickCount = var.tickCount;			// Set Last Count To Current Count

			SwapBuffers(hDC);
		}
	}

	KillGLWindow();
	return (msg.wParam);
}
